package com.rzx.mybackend.config.rabbitMQ;

import com.rzx.mybackend.event.mqRouteEvent.MessageFailedEvent;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * RabbitMQConfig
 * 企业级别 MQ配置（待细化）
 * @author renzhexian
 * @date 2025/7/23
 */
@Configuration
@RequiredArgsConstructor
public class RabbitMQConfig {

    private static final Logger log = LoggerFactory.getLogger(RabbitMQConfig.class);

//    private final ApplicationEventPublisher eventPublisher;
    // 评论队列配置
    @Value("${spring.rabbitmq.comment.queue_name}")
    private String COMMENT_QUEUE_NAME;
    @Value("${spring.rabbitmq.comment.exchange_name}")
    private String COMMENT_EXCHANGE_NAME;
    @Value("${spring.rabbitmq.comment.routing_key}")
    private String COMMENT_ROUTING_KEY;
    // 订单超时死信队列配置
    @Value("${spring.rabbitmq.timeOrderDead.queue_name}")
    private String TIMEORDER_QUEUE_NAME;
    @Value("${spring.rabbitmq.timeOrderDead.exchange_name}")
    private String TIMEORDER_EXCHANGE_NAME;
    @Value("${spring.rabbitmq.timeOrderDead.routing_key}")
    private String TIMEORDER_ROUTING_KEY;
    // 订单延时队列配置
    @Value("${spring.rabbitmq.delayOrder.queue_name}")
    private String DELAY_QUEUE_NAME;
    @Value("${spring.rabbitmq.delayOrder.exchange_name}")
    private String DELAY_EXCHANGE_NAME;
    @Value("${spring.rabbitmq.delayOrder.routing_key}")
    private String DELAY_ROUTING_KEY;
    // 游戏发布延时队列配置
    @Value("${spring.rabbitmq.gamePublish.queue_name}")
    private String GAME_PUBLISH_DELAY_QUEUE;
    @Value("${spring.rabbitmq.gamePublish.exchange_name}")
    private String GAME_PUBLISH_DELAY_EXCHANGE;
    @Value("${spring.rabbitmq.gamePublish.routing_key}")
    private String GAME_PUBLISH_DELAY_ROUTING_KEY;


    /*******************************   基本配置   ********************************/
    // 使用JSON 序列化
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    // 注册RabbitTemplate，并设置消息转换器
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(messageConverter());

        // 配置重试机制
        RetryTemplate retryTemplate = new RetryTemplate();

        // 设置重试策略
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(1000);  // 初始间隔1秒
        backOffPolicy.setMultiplier(2.0);        // 指数退避倍数
        backOffPolicy.setMaxInterval(30000);     // 最大间隔30秒
        retryTemplate.setBackOffPolicy(backOffPolicy);

        // 设置重试次数
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);  // 最多重试3次
        retryTemplate.setRetryPolicy(retryPolicy);

        rabbitTemplate.setRetryTemplate(retryTemplate);


        /*
        确认消息是否成功发送到 rabbitMQ Broker
        成功打印日志
        失败进行重试
     */

        // 设置消息发送确认回调，用于确认消息是否成功发送到交换机
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.info("消息成功发送到 Broker，ID: {}", correlationData != null ? correlationData.getId() : "无 ID");
            } else {
                log.error("消息发送失败，ID: {}, 原因: {}", correlationData != null ? correlationData.getId() : "无 ID", cause);

                // 如果correlationData为空，代表为重试消息，则不进行重试
//                if (correlationData == null) {
//                    log.error("消息发送失败");
//                    return;
//                }
                // 获取失败消息ID
//                String msgId = correlationData.getId();

                // 事件驱动模式
//                eventPublisher.publishEvent(new MessageFailedEvent(this, msgId, cause));


            }
        });

        // 设置消息退回回调，当消息无法路由到队列时触发
        rabbitTemplate.setReturnsCallback(returned -> {
            System.out.println("消息退回信息: " + returned.getMessage());
        });

        return rabbitTemplate;
    }
/*******************************   评论队列  ********************************/
    /**
     * 创建评论队列
     * @return Queue对象
     */
    @Bean
    public Queue commentQueue() {
        return new Queue(COMMENT_QUEUE_NAME, true); // durable设置为true表示持久化队列
    }

    /**
     * 创建评论交换机
     * @return DirectExchange对象
     */
    @Bean
    public DirectExchange commentExchange() {
        return new DirectExchange(COMMENT_EXCHANGE_NAME, true, false); // durable=true, autoDelete=false
    }


    /**
     * 绑定队列与交换机
     * @param commentQueue 队列实例
     * @param commentExchange 交换机实例
     * @return Binding对象
     */
    @Bean
    public Binding commentBinding(Queue commentQueue, DirectExchange commentExchange) {
        return BindingBuilder.bind(commentQueue)
                .to(commentExchange)
                .with(COMMENT_ROUTING_KEY);
    }

/*******************************   订单超时死信队列  ********************************/
    /**
     * 创建订单队列
     * @return Queue对象
     */
    @Bean
    public Queue orderDlq() {
        return new Queue(TIMEORDER_QUEUE_NAME, true);
    }

    /**
     * 创建订单交换机
     * @return DirectExchange对象
     */
    @Bean
    public DirectExchange orderDlxExchange() {
        return new DirectExchange(TIMEORDER_EXCHANGE_NAME, true, false);
    }

    /**
     * 绑定队列与交换机
     * @param orderDlq 队列实例
     * @param orderDlxExchange 交换机实例
     * @return Binding对象
     */
    @Bean
    public Binding dlxBinding(Queue orderDlq, DirectExchange orderDlxExchange) {
        return BindingBuilder.bind(orderDlq)
                .to(orderDlxExchange)
                .with(TIMEORDER_ROUTING_KEY);
    }

/*******************************   订单延时队列  ********************************/

    /**
     * 创建延时队列，此处死信队列的key是固定的，不能自定义，如果改了会导致死信队列失效
     * @return
     */
    @Bean
    public Queue orderDelayQueue() {
        Map<String, Object> args = new HashMap<>();
        // 设置死信交换机 - 当消息过期后会发送到这个交换机
        args.put("x-dead-letter-exchange", TIMEORDER_EXCHANGE_NAME);
        // 设置死信路由键 - 过期消息会用这个路由键路由
        args.put("x-dead-letter-routing-key", TIMEORDER_ROUTING_KEY);
        // 注意：这里不设置固定TTL，而是在发送消息时设置
        return new Queue(DELAY_QUEUE_NAME, true, false, false, args);
    }

    /**
     * 创建延时交换机
     * @return
     */
    @Bean
    public DirectExchange orderDelayExchange() {
        return new DirectExchange(DELAY_EXCHANGE_NAME, true, false);

    }

    /**
     * 绑定延时队列与延时交换机
     * @param orderDelayQueue
     * @param orderDelayExchange
     * @return
     */
    @Bean
    public Binding delayBinding(Queue orderDelayQueue, DirectExchange orderDelayExchange) {
        return BindingBuilder.bind(orderDelayQueue)
                .to(orderDelayExchange)
                .with(DELAY_ROUTING_KEY);
    }
/*******************************   定时发布游戏队列  ********************************/

    /**
     * 创建定时发布游戏队列
     * @return
     */
    @Bean
    public Queue gamePublishQueue() {
        return new Queue(GAME_PUBLISH_DELAY_QUEUE, true);
    }

    /**
     * 创建定时发布游戏交换机
     * @return
     */
    @Bean
    public CustomExchange gamePublishExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(
                GAME_PUBLISH_DELAY_EXCHANGE,
                "x-delayed-message",
                true,
                false,
                args);
    }

    /**
     * 绑定定时发布游戏队列与定时发布游戏交换机
     * @param gamePublishQueue
     * @param gamePublishExchange
     * @return
     */
    @Bean
    public Binding gamePublishBinding(Queue gamePublishQueue, CustomExchange gamePublishExchange) {
        return BindingBuilder.bind(gamePublishQueue)
                .to(gamePublishExchange)
                .with(GAME_PUBLISH_DELAY_ROUTING_KEY)
                .noargs();
    }
}
